//! 1. 原始数据类型
//
const a: string = '111'; //只能存字符串
const b: number = 100; //NaN Infinity
const c: boolean = true; //false
// ts中上三种可为空（null或者undefined），严格不行

// const d: boolean = null;
const e: void = undefined;
//ts中，void类型可无undefined或null(严格只能undefined)

const f: null = null;
const g: undefined = void 0;
const h: symbol = Symbol();

//!  标准库声明 ：就是内置对象所对应的声明
console.log(111);
//! 中文错误提示
// yarn tsc --locale zh-CN
// vscode也可设置
//! 作用域问题 全局变量会在不同文件冲突
// 解决方法：放在函数中；或者变成一个es Module
//! 2. Object类型
// object类型不单指普通对象，泛指所有非原始类型。可以是函数、对象、数组
// const foo:object = //[]//{}//function() {}
// 使用字面量方式去单指普通对象
const obj1: { a: number; b: number } = { a: 1, b: 2 };
// 更专业的用法是接口
//! 3. 数组类型
// 和flow一致
//* 方式1：array泛型
const arr1: Array<number> = [1];
//* 方式2：类似字面量方式
const arr2: number[] = [2];
// ts好处小体会
function sum(...args: number[]) {
  return args.reduce((prev, current) => prev * current, 0);
}
// sum(1, 2, '3');
//! 3.1 元组类型 ：明确成员数量和类型的数组
const tuple: [number, string] = [1, '2'];
// 元组应用场景：
Object.entries({
  age: 12,
  name: 'zp',
}); //得到每个键、值都是元组
//! 4. 枚举类型
/* const sex = {
  man: 1,
  woman: 2,
}; */
// * 数字枚举
//不指定值，从0开始累加
//指定第一个值，从第一个值开始累加
/* enum sex1 {
  man = 1,
  woman = 2,
} */
// * 字符串枚举（不常见）
// * 枚举类型入侵运行时代码
// 会生成双向的键值对对象。键获取值，值获取键
// * 保证无侵入，使用常量枚举。值会被替换

const enum Sex2 {
  man = 1,
  woman = '2',
}

const mySex = Sex2.man;
//! 5. 函数类型
// * 写法一：函数声明
// 实参、形参个数、类型必须一致
function fn1(a: number, b: string) {}
// 可选参数（放最后）
// 默认值（相当于可选，放最后）
// 任意个数参数
function fn2(...args: number[]) {}
// * 写法二：函数表达式
// 回调函数 定义接口常用
//! 6. 任意类型
// 动态类型
function stringify(value: any) {
  return JSON.stringify(value);
}
//! 7. 隐式类型推断
let age = 11;
// age = 'aa' //推断为number,尽量不要隐式，明确的写出来
//! 8. 类型断言
// 很确定是什么类型
const nums = [110, 120, 130];
const res = nums.find((i) => i > 0);

// 方法一：
const num1 = res as number; //推荐
// 方法二：
const num2 = <number>res; //jsx语法冲突
const square = num1 * num1;
console.log(square);
//! 9. 接口：约束普通对象的结构
interface Post {
  title: string;
  content: string;
  subTitle?: string; // * 可选成员
  readonly summary?: string; // * 只读成员
}
function printPost(post: Post) {
  console.log(post.title);
}
printPost({ title: '11', content: '22' });

// * 动态成员
// 缓存对象
interface cache {
  [prop: string]: string;
}
//! 10. 类：用来描述一类具体对象的抽象成员
// * 类属性必须先声明
class Person {
  name: string;
  /**
   * //* 四个访问修饰符：
   * 1. private私有属性（只允许当前类访问）；
   * 2. 默认public；
   * 3. protected 受保护的（只允许子类访问，允许继承）
   * 4. readonly
   */
  private age: number;
  // 控制内部成员的访问级别
  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
  sayHi(msg: string): void {}
}
// 相同的特性 --- 协议 --- 接口
//! 10.1 类接口
interface EatAndRun {
  eat(food: string): void;
  run(distance: number): void;
}
class Person2 implements EatAndRun {
  // class Person2 implements Eat,Run { //建议拆分写
  eat() {}
  run() {}
}
//! 10.2 抽象类 ：只能被继承，不能实例化
abstract class Animal {
  eat(food: string) {
    console.log('进食');
  }
  /**
   * 抽象方法
   * 1.不需要函数体
   * 2.子类必须去实现函数体。
   */
  abstract run(distance: number): void;
}

class Dog extends Animal {
  run(distance: number) {
    console.log('跑步');
  }
}

const d = new Dog();
d.eat('aaa');
d.run(100);

//! 11. 泛型写法 <T>
function createNumberArray(length: number, value: number): number[] {
  // Array泛型类 后面加泛型参数
  const arr = Array<number>(length).fill(value);
  return arr;
}

createNumberArray(3, 100); //[100,100,100] 有局限性

// 泛型改造:泛型参数用T名称
function createArray<T>(length: number, value: T): T[] {
  const arr = Array<T>(length).fill(value);
  return arr;
}
const res1 = createArray<string>(3, 'foo');

//! 12. 类型声明 declare
// declare function camelCase(input: string): string;
